/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/***********************************************************************
 *
 * FILENAME:
 * nwsWarnings.cxx
 *
 * PURPOSE:
 *	Provide the NWS Warnings shapefile interface
 *
 * LAST MODIFIED:
 *
 ***********************************************************************/

#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <gdk/gdkkeysyms.h>
#include <libshp/shapefil.h>

#include "gtkMap.h"
#include "nwsWarnings.h"
#include "color.h"

const char *NWSWarnings::warn_files[NUM_WARN] = { WARN_DIR"/SVR.shp", WARN_DIR"/TOR.shp",  WARN_DIR"/FFW.shp"};
const char *NWSWarnings::warn_color[NUM_WARN] = { "sea green", "red", "blue"};
const char *NWSWarnings::warn_names[NUM_WARN] = { "Severe Thunderstorm", "Tornado Warnings", "Flash Flood"};

NWSWarnings::NWSWarnings() 
{
	initialized = false;
	haveNewWarn = false;
	line_width = 3.0;
}

NWSWarnings::~NWSWarnings() 
{
	if( soup_session ) {
		soup_session_abort(soup_session);
		g_object_unref(soup_session);
	}
	soup_session = NULL;
}
	
void NWSWarnings::init(GPSMap *map)
{
	//cout << "NWSWarnings::init" << endl;

	// init base class
	LayerManager::init(map);

	// get my layer group
	layerG = GOO_CANVAS_ITEM(gps_map->getLayerG(GPSMap::NWS_WARN_LAYER));
}

void NWSWarnings::updateMap()
{
	//cout << "NWSWarnings::updateMap()" << endl;
}

bool NWSWarnings::setShown(int setTo)
{
	//cout << "NWSWarnings::setShown setTo=" << setTo << endl;

	LayerManager::setShown(setTo);
	if(!initialized && setTo) initWarnings();

	return true;
}

void NWSWarnings::translate_from_zoomChange(double zoom_factor)
{
	//cout << "NWSWarnings::translate_from_zoomChange - zoom_factor=" << zoom_factor << " isShown=" << isShown << endl;
	if(!isShown) return;

	// to maintain accuracry, simply reparse files
	if( haveNewWarn )
		parseWarnings();
}


void NWSWarnings::initWarnings()
{
	cout << "Initializing NWS Warnings : ...................";

	// set the layer as active and shown
	gps_map->setLayerListShown( (GooCanvasItem*)GPSMap::NWS_WARN_LAYER, true );

	for(int i=0; i < (int)NUM_WARN; i++ ) {
		warn_groups[i] = goo_canvas_group_new (layerG, NULL);

		// add the image to the layer list
		gps_map->addLayer( warn_names[i], warn_groups[i], (GooCanvasItem *)GPSMap::NWS_WARN_LAYER );
		gps_map->setLayerListShown(warn_groups[i], true );
	}

	// make sure parent manager layer is shown
	gps_map->toggleLayerShown(GPSMap::NWS_WARN_LAYER, true);

	// stop any previous session
	if( soup_session ) {
		soup_session_abort(soup_session);
		g_object_unref(soup_session);
	}

	// create soup session
	soup_session = soup_session_async_new_with_options(
			SOUP_SESSION_MAX_CONNS_PER_HOST, 10,
			SOUP_SESSION_USER_AGENT,
			"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
			NULL);
	cout << " [" GREEN << "done" << ATTROFF "]" << endl;

	// get the warning file
	updateWarningFile();

	// update warning file every 5 mins
	g_timeout_add (5*60*1000, NWSWarnings::update_warn_timeout, this);

	initialized = true;
}

void NWSWarnings::updateWarningFile()
{
	download_t *dl = g_new0(download_t, 1);

	if( !dl ) {
		g_warning("Out of memory");
		return;
	}
		
	dl->uri      = g_strdup_printf("http://www.weather.gov/regsci/gis/shapefiles/current_warnings.zip");
	dl->folder   = g_strdup_printf(WARN_DIR);
	dl->filename = g_strdup_printf(WARN_ZIP_FILE);
	dl->me       = this;

	SoupMessage *msg;
	msg = soup_message_new (SOUP_METHOD_GET, dl->uri);
	if (msg) {
		soup_session_queue_message (soup_session, msg, NWSWarnings::download_complete, dl);
	} else {
		g_warning("\tCould not create soup message");
		g_free(dl->uri);
		g_free(dl->folder);
		g_free(dl->filename);
		g_free(dl);
	}
}

void NWSWarnings::download_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
{
	int fd;
	download_t *dl = (download_t *)user_data;

	//cout << "NWSWarnings::download_complete : " << dl->filename << endl;
	if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
	{
		if (g_mkdir_with_parents(dl->folder,0700) == 0)
		{
			fd = open(dl->filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
			if (fd != -1)
			{
				// do some file processing to unzip file
				if ( msg->response_body->length > 0 )
				{
					write (fd, msg->response_body->data, msg->response_body->length);
					close (fd);
					//g_debug("Wrote %lld bytes to %s", msg->response_body->length, dl->filename);
					
					if( system("unzip -o "WARN_ZIP_FILE" -d "WARN_DIR" > /dev/null") < 0 )
						g_warning("Did not unzip file");
					else {
						dl->me->haveNewWarn = true;
						dl->me->parseWarnings();
					}

				} else {
					g_warning("Downloaded zero length file");
				}
			} else {
				g_warning("Error creating downloaded zip file : %s", dl->filename);
			}
		} else {
			g_warning("Error creating tile download directory: %s", dl->folder);
		}
	} else {
		g_warning("Error downloading zip file: %d - %s", msg->status_code, msg->reason_phrase);
	}

	g_free(dl->uri);
	g_free(dl->folder);
	g_free(dl->filename);
	g_free(dl);
}

void NWSWarnings::parseWarnings()
{
	//cout << "NWSWarnings::parseWarnings" << endl;

	for(int i=0; i < (int)NUM_WARN; i++ ){
		if( poly_groups[i] ) goo_canvas_item_remove(poly_groups[i]);
		poly_groups[i] = goo_canvas_group_new (warn_groups[i], NULL);
		g_object_set (poly_groups[i], "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);

		parseWarnFile((WarnType_t)i);
	}
}

void NWSWarnings::parseWarnFile(WarnType_t warn)
{
	//cout << "NWSWarnings::parseWarnFile - file=" << warn_files[warn] << endl;

	SHPHandle shp_h = SHPOpen(warn_files[warn], "rb");
	if( !shp_h ) {
		g_warning("Did not open shapfile: %s", warn_files[warn]);
		return;
	}

	int pnEntities, pnShapeType;
	double  padfMinBound[4], padfMaxBound[4];
	SHPGetInfo( shp_h, &pnEntities, &pnShapeType, padfMinBound, padfMaxBound );

	//cout << "\tpnEntities="<< pnEntities << endl;

	for(int iShape=0; iShape < pnEntities; iShape++) {

		SHPObject *psShape = SHPReadObject( shp_h, iShape );
		if( !psShape ) {
			g_warning("Illegal shape object requested");
			continue;
		}
/*
		printf( "\nShape:%d (%s)  nVertices=%d, nParts=%d\n"
				"  Bounds:(%12.3f,%12.3f, %g, %g)\n"
				"      to (%12.3f,%12.3f, %g, %g)\n",
				iShape, SHPTypeName(psShape->nSHPType),
				psShape->nVertices, psShape->nParts,
				psShape->dfXMin, psShape->dfYMin,
				psShape->dfZMin, psShape->dfMMin,
				psShape->dfXMax, psShape->dfYMax,
				psShape->dfZMax, psShape->dfMMax );

		for( int j = 0, iPart = 1; j < psShape->nVertices; j++ )
		{
			const char  *pszPartType = "";

			if( j == 0 && psShape->nParts > 0 )
				pszPartType = SHPPartTypeName( psShape->panPartType[0] );

			if( iPart < psShape->nParts && psShape->panPartStart[iPart] == j )
			{
				pszPartType = SHPPartTypeName( psShape->panPartType[iPart] );
				iPart++;
			}

			printf("\t(%12.3f,%12.3f, %g, %g) %s \n",
					psShape->padfX[j],
					psShape->padfY[j],
					psShape->padfZ[j],
					psShape->padfM[j],
					pszPartType );
		}
*/
		int zoom = gps_map->getMapZoom();
		if( zoom < 3 )
			line_width = 1.0;
		else if( zoom < 6 )
			line_width = 2.0;
		else
			line_width = 3.0;

		switch(psShape->nSHPType) {
			case SHPT_NULL:
				break;

			case SHPT_POLYGON: {
				int j = 0;
				double x0 = psShape->padfX[j];
				double y0 = psShape->padfY[j];

				// transform into window
				gps_map->geoToLayer(GPSMap::RADAR_LAYER, x0, y0);

				for( int j = 1; j < psShape->nVertices; j++ )
				{
					double x1 = psShape->padfX[j];
					double y1 = psShape->padfY[j];

					// transform into window
					gps_map->geoToLayer(GPSMap::RADAR_LAYER, x1, y1);
					(void)goo_canvas_polyline_new_line(poly_groups[warn],
								x0, y0, x1, y1,
							  "stroke-color", warn_color[warn],
							  "line-width", line_width,
							  NULL);

					x0 = x1;
					y0 = y1;
				}

				break;
			}
			default:
				cout << SHPTypeName( psShape->nSHPType ) << " -- NOT HANDLED" << endl;
		}


		SHPDestroyObject(psShape);
	}

	//cout << "closing ... " << endl;
	SHPClose(shp_h);
}

gboolean NWSWarnings::update_warn_timeout(gpointer data)
{
	//cout << "RadarManager::update_warn_timeout" << endl;

	if( !data ) return false;
	NWSWarnings *me = (NWSWarnings*) data;

	me->updateWarningFile();
	
	return true;
}
